package org.bookshare.document.navigation.chapters;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.benetech.collections.IdTree;
import org.jdom.Element;

/**
 * A list of chapters with statistical metadata.
 * @author Reuben Firmin
 */
public final class Chapters implements Iterable<Chapter> {

	private List<Chapter> list;
	private int numChaptersConfirmed;
	private double cumulativeDistance;
	private double confirmScore;
	private String description;

	/**
	 * A raw set of references into the document. A list of {@link Chapter} objects will be derived.
	 * @param containingTree The tree containing all of these chapters, i.e. the top-most node.
	 * @param chaptersAsList The list of references
	 * @param description The description of how these were derived.
	 */
	public Chapters(final IdTree<Element, String, String> containingTree,
			final List<IdTree<Element, String, String>> chaptersAsList, final String description)
	{
		// XXX containingTree no longer used, but distinguishes the constructor args
		list = new ArrayList<Chapter>();
		this.description = description;
		for (IdTree<Element, String, String> chapter : chaptersAsList) {
			final Chapter ch = new Chapter(chapter);
			list.add(ch);
		}
	}

	/**
	 * There is already a list of chapters, with text applied.
	 * @param chaptersAsList The chapters
	 * @param description The description of how these were derived
	 */
	public Chapters(final List<Chapter> chaptersAsList, final String description) {
		this.description = description;
		this.list = chaptersAsList;
	}

	/**
	 * Reset the statistics.
	 */
	public void reset() {
		this.confirmScore = 0;
		this.cumulativeDistance = 0;
		this.numChaptersConfirmed = 0;
	}

	/**
	 * Get the confirm score. Any number between 0 and {@link #size()}, with the maximum representing that all chapters
	 * were confirmed in other chapter sets.
	 * @return The confirm score
	 */
	public double getConfirmScore() {
    	return confirmScore;
    }

	/**
	 * Increment the confirm score. The total confirm score can be up to 1 per chapter, representing that it was
	 * confirmed in 100% of other chapter sets.
	 * @param confirmScore The amount to confirm by
	 */
	public void incrConfirmScore(final double confirmScore) {
    	this.confirmScore += confirmScore;
    	if (confirmScore > size()) {
    		throw new RuntimeException("Confirm score cannot grow greater than 1 per chapter");
    	}
    }

	/**
	 * Get the cumulative distance. This is the total distance between all the chapters in this set, and the chapters
	 * in the other sets (i.e. the minimum for each, summed).
	 * @return The distance
	 */
	public double getCumulativeDistance() {
    	return cumulativeDistance;
    }

	/**
	 * Description of this set of chapters, likely how they were derived.
	 * @return Never null
	 */
	public String getDescription() {
    	return description;
    }

	/**
	 * The number of chapters confirmed in other candidates.
	 * @return number
	 */
	public int getNumChaptersConfirmed() {
    	return numChaptersConfirmed;
    }

	/**
	 * Confirm this chapter. This affects the results of {@link #getCumulativeDistance()} and
	 * {@link #getNumChaptersConfirmed()}. The minimum distance should be set on the incoming chapter.
	 * @param chapter The chapter to confirm
	 */
	public void confirmChapter(final Chapter chapter) {
    	this.numChaptersConfirmed++;
    	this.cumulativeDistance += chapter.getMinDistance();
    	if (this.numChaptersConfirmed > size()) {
    		throw new RuntimeException("Faulty logic - cannot confirm more chapters than there are");
    	}
    	if (!list.contains(chapter)) {
    		throw new RuntimeException("Cannot confirm a chapter that doesn't belong to this set");
    	}
    }

	/**
	 * The number of chapters.
	 * @return the size
	 */
	public int size() {
		return list.size();
	}

	/**
	 * Get the chapter at the given index.
	 * @param index 0 to {@link #size()} - 1
	 * @return never null
	 */
	public Chapter get(final int index) {
		return list.get(index);
	}

	/**
	 * {@inheritDoc}
	 */
	public boolean equals(final Object other) {
		if (!(other instanceof Chapters)) {
			return false;
		}
		return list.equals(((Chapters) other).list);
	}

	/**
	 * {@inheritDoc}
	 */
	public int hashCode() {
		return list.hashCode();
	}

	/**
	 * {@inheritDoc}
	 */
	public Iterator<Chapter> iterator() {
		return list.iterator();
    }

	/**
	 * Remove this chapter. Cannot call concurrently while iterating.
	 * @param chapter The chapter to remove
	 */
	public void remove(final Chapter chapter) {
		list.remove(chapter);
	}
}
